module dgl.graphics.MeshContainer;

import tango.text.convert.Integer;
import tango.text.convert.Float;
import tango.text.Util;
import tango.io.Console;
import tango.math.Math;

import derelict.opengl.gl;

import dgl.BoundingBox;
import dgl.BoundingSphere;
import dgl.graphics.GraphicsDevice;
import dgl.graphics.IDrawable;
import dgl.graphics.LoadHelper;
import dgl.graphics.Model;
import dgl.graphics.ModelMesh;

import dgl.Matrix;
import dgl.Vector3;
import dgl.GameTime;

class MeshContainer : IDrawable
{
	private uint m_index;
	
	private char[] m_name;
	
	private MeshContainer m_parent;
	
	private char[] m_parentName;
	
	private Matrix m_matLocal;
	
	private Matrix m_matAbsolute;
	
	private Matrix m_matWorld;
	
	private ModelMesh m_mesh;	
	
	public uint index()
	{
		return m_index;
	}
	
	public char[] name()
	{
		return m_name;
	}
	
	public MeshContainer parent()
	{
		return m_parent;
	}
	
	package char[] parentName()
	{
		return m_parentName;
	}
	
	public Matrix matLocal()
	{
		return m_matLocal;
	}
	
	public Matrix absoluteTransform()
	{
		return m_matAbsolute;
	}
	
	public Matrix matWorld()
	{
		return m_matWorld;
	}

	public BoundingBox aabb()
	{
		return this.calcTransformedAABB();
	}
	
	public BoundingSphere boundingSphere()
	{
		return this.calcTransformedBoundingSphere();
	}
	
	public ModelMesh mesh()
	{
		return m_mesh;
	}
	
	public void index( uint index )
	{
		m_index = index;
	}
	
	public void name( char[] name )
	{
		m_name = name;
	}
	
	package void parent( MeshContainer parent )
	{
		m_parent = parent;
	}
	
	package void parentName( char[] parentName )
	{
		m_parentName = parentName;
	}
	
	package void matLocal( Matrix matLocal )
	{
		m_matLocal = matLocal;
	}
	
	public void matWorld( Matrix matWorld )
	{
		m_matWorld = matWorld;
	}
		
	package void mesh( ModelMesh mesh )
	{
		m_mesh = mesh;
	}
	
	public this()
	{
		m_index = 0;
		m_matLocal = Matrix.identity;
		m_matWorld = Matrix.identity;
		m_mesh = null;
		m_name = "";
		m_parent = null;
		m_parentName = "";
	}
	
	public static MeshContainer loadFromFile( LoadHelper loadHelper, Model model )
	{
		char[] data;
		MeshContainer ret = new MeshContainer();
		
		if( loadHelper.readLine( data ) == "\tIndex" )
		{
			ret.index = toInt( data );
		}
		bool hasParent = false;
		if( loadHelper.readLine( data ) == "\tHasParent" )
		{
			hasParent = cast(bool)( toInt( data ) );
		}
		bool hasFaceUV = false;
		if( loadHelper.readLine( data ) == "\tHasFaceUV" )
		{
			hasFaceUV = cast(bool)( toInt( data ) );
		}
		if( loadHelper.readLine( data ) == "\tName" )
		{
			ret.name = data;
		}
		if( hasParent )
		{
			if( loadHelper.readLine( data ) == "\tParentName" )
			{
				ret.parentName = data;
			}
		}
		else
		{
			ret.parentName = null;
		}
		Matrix matLocal;
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine0" )
		{
			char[][] elements = split( data, " " );
			matLocal.m11 = toFloat( elements[0] );
			matLocal.m12 = toFloat( elements[1] );
			matLocal.m13 = toFloat( elements[2] );
			matLocal.m14 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine1" )
		{
			char[][] elements = split( data, " " );
			matLocal.m21 = toFloat( elements[0] );
			matLocal.m22 = toFloat( elements[1] );
			matLocal.m23 = toFloat( elements[2] );
			matLocal.m24 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine2" )
		{
			char[][] elements = split( data, " " );
			matLocal.m31 = toFloat( elements[0] );
			matLocal.m32 = toFloat( elements[1] );
			matLocal.m33 = toFloat( elements[2] );
			matLocal.m34 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine3" )
		{
			char[][] elements = split( data, " " );
			matLocal.m41 = toFloat( elements[0] );
			matLocal.m42 = toFloat( elements[1] );
			matLocal.m43 = toFloat( elements[2] );
			matLocal.m44 = toFloat( elements[3] );
		}
		ret.matLocal = matLocal;
		
		ret.mesh = ModelMesh.loadFromFile( loadHelper, ret, hasFaceUV );
			
		return ret;
	}
	
	public void calculateAbsoluteTransform()
	{
		if( m_parent )
		{
			m_parent.calculateAbsoluteTransform();
			m_matAbsolute = m_matLocal * m_parent.absoluteTransform;
		}
		else
		{
			m_matAbsolute = m_matLocal;
		}
	}
	
	private BoundingBox calcTransformedAABB()
	{
		BoundingBox ret;
		Vector3 pos1 = Vector3.transform( m_mesh.aabb.min, m_matAbsolute * m_matWorld );
		Vector3 pos2 = Vector3.transform( m_mesh.aabb.max, m_matAbsolute * m_matWorld );
		ret.min.x = min( pos1.x, pos2.x );
		ret.max.x = max( pos1.x, pos2.x );
		ret.min.y = min( pos1.y, pos2.y );
		ret.max.y = max( pos1.y, pos2.y );
		ret.min.z = min( pos1.z, pos2.z );
		ret.max.z = max( pos1.z, pos2.z );
		return ret;
	}
	
	private BoundingSphere calcTransformedBoundingSphere()
	{
		BoundingSphere ret;
		ret.center = Vector3.transform( m_mesh.boundingSphere.center, m_matAbsolute * m_matWorld );
		ret.radius = m_mesh.boundingSphere.radius;
		return ret;
	}
	
	public void draw( GameTime gameTime )
	{
		glMatrixMode( GL_MODELVIEW );
		Matrix modelView = m_matAbsolute * m_matWorld * GraphicsDevice.matView;
		glLoadMatrixf( modelView.ptr );
		
		m_mesh.draw( gameTime );
	}
}